#include<stdio.h>
#include<stdlib.h>
#include<getopt.h>

/*
getopt.h中关键的全局变量：
	extern char* optarg; 保存选项的参数，用法见范例
	extern int optind; 记录下一个检索位置
	extern int opterr; 是否将错误信息输出到stderr，为0时表示不输出
	extern int optopt; 不在选项字符串optstring中的选项
*/

/*
关键函数：
int getopt_long(int argc, char * const argv[], 
const char *optstring,
const struct option *longopts, int *longindex);

argc,argv通常直接给main函数的就行
optstring 描述了可以接受的选项:
	1.单个字符，表示选项，
	2.单个字符后接一个冒号：表示该选项后必须跟一个参数。参数紧跟在选项后或者以空格隔开。该参数的指针赋给optarg。
	3 单个字符后跟两个冒号，表示该选项后可以有参数也可以没有参数。如果有参数，参数必须紧跟在选项后不能以空格隔开。该参数的指针赋给optarg。
longopts 描述长选项
*/

//描述长选项
struct option arg_options[] = {
    { "a_param", required_argument, NULL, 'a' }, //可以使用 --a_param 或者 -a，且需要参数
    { "b_param", no_argument, NULL, 'b' }, //可以使用 --b_param 或者 -b，且不需要参数
    { "c_param", optional_argument, NULL, 'c' }, //可以使用 --c_param 或者 -c，参数可选
    { "help", no_argument, NULL, 'h' }, //带个help让程序优雅一点
    { 0 },
};

void print_help() {
	//可以在help里面提示用户各个选项的作用，虽然最后还是要在代码里面处理参数
    printf("cmdline_process \n\n");
    printf("  -a, --a_param=NUMBER  number\n"
           "  -b, --b_param         no argument\n"
		   "  -c, --c_param         optional argument\n"
           "  -h, --help            show help message\n");
}

void parse_args(int argc, char **argv) {
    int opt;
    opterr = 0; //令错误信息不输出到stderr，也可以去掉试试
    char *ptr = NULL;
	int a_param = 0;

	//一个循环，分析完所有命令行参数才会结束
	//a:bc::h的意义，结合上文，就是-a需要参数，-b和-h不需要参数，-c可选参数且不带空格，当然getopt_long还支持长选项，见arg_options
    while ((opt = getopt_long(argc, argv, "a:bc::h", arg_options, NULL)) != -1) {
		/*
			返回值opt为分析到的选项，此时全局变量optarg的指针指向和该选项配对的参数
			比如-a 1234 -b 2345，opt=='a'时，optarg指向"1234"字符串，
			当然参数值具体还是要代码里自行处理的
			此处示范为主，不进行比较详尽的参数合法性判断

			异常情况：如果你输入意外的选项，返回值是'?'
		*/
        switch (opt) {
            case 'a':
                a_param = strtol(optarg, &ptr, 10);
				printf("option a, argument: %d\n", a_param);
                break;
            case 'b':
                printf("option b\n");
                break;
			case 'c':
				printf("option c, argument: %s\n", optarg);
				break;
            case 'h':
                print_help();
                exit(-1);
            case '?':
            case ':':
            default:
                print_help();
                exit(-1);
        }
    }
}

int main(int argc, char **argv) {
    parse_args(argc, argv);
    return 0;
}
